package com.serverlite.framework.servlet.core;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.Enumeration;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.json.JSONException;
import org.json.JSONObject;

import com.serverlite.framework.servlet.mapping.ClassMethod;
import com.serverlite.framework.servlet.mapping.RouteMapping;
import com.serverlite.framework.utils.PropertiesUtils;
import com.serverlite.framework.utils.StringUtils;

public class BaseServlet extends HttpServlet {

    /**
     * Print invoke info or not.
     */
    public static boolean showInvoke = true;

    private static final long serialVersionUID = 1L;

    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        invoke(request, response);
    }

    protected void doPost(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        invoke(request, response);
    }

    protected void invoke(HttpServletRequest request,
            HttpServletResponse response) {

        showInvoke = Boolean.valueOf(PropertiesUtils.get("show_invoke"));

        Map<String, ClassMethod> routeMap = RouteMapping.getMap();

        // Set request,response encode.
        setEncoding(request, response);

        // Get request URI.
        String uri = request.getRequestURI();

        // Get context path.
        String contextPath = request.getContextPath();

        // Delete context path from HTTP URI.
        String classMethod = uri.replace(contextPath, "");

        ClassMethod cm = routeMap.get(classMethod);
        
        if(cm == null){
            return;
        }

        // Print invoke info.
        if (showInvoke) {
            System.out.println("invoke " + cm.getClazz().getName() + "." + cm.getMethod().getName() + "()");
        }

        // Change request parameters to JSON Object.
        JSONObject jsonParam = parseParam(request);

        // Print request parameters.
        if (showInvoke) {
            System.out.println("param " + jsonParam);
        }

        try {
            Object returnValue = null;

            // Instance class.
            Object instance = cm.getClazz().newInstance();

            Class<?>[] params = cm.getMethod().getParameterTypes();
            /**
             * Set invoke parameter. There have three type
             * parameter(request,response,JSON).
             */
            Object[] arguments = new Object[params.length];
            int i = 0;
            for (Class<?> param : params) {
                if (param == HttpServletRequest.class) {
                    arguments[i] = request;
                } else if (param == HttpServletResponse.class) {
                    arguments[i] = response;
                } else if (param == JSONObject.class) {
                    arguments[i] = jsonParam;
                }
                i++;
            }
            // Invoke method and return value.
            returnValue = cm.getMethod().invoke(instance, arguments);

            if (returnValue != null) {
                if (returnValue instanceof WebPage) {
                    // return a web page.
                    WebPage webPage = (WebPage) returnValue;
                    if (!StringUtils.isEmpty(webPage.getForwardPath())) {
                        // dispatcher page.
                        request.getRequestDispatcher(webPage.getForwardPath())
                                .forward(request, response);
                    } else if (!StringUtils.isEmpty(webPage.getRedirectPath())) {
                        // redirect page.
                        response.sendRedirect(webPage.getRedirectPath());
                    }
                } else {
                    // return a text.
                    PrintWriter writer = response.getWriter();
                    writer.write(returnValue.toString());
                    writer.flush();
                    writer.close();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Change request parameters to JSON Object.
     */
    private JSONObject parseParam(HttpServletRequest request) {
        JSONObject jsonParam = new JSONObject();
        Enumeration<String> parameterNames = request.getParameterNames();
        while (parameterNames.hasMoreElements()) {
            String parameterName = parameterNames.nextElement();
            String parameterValue = request.getParameter(parameterName);
            try {
                jsonParam.put(parameterName, parameterValue);
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
        return jsonParam;
    }

    /**
     * Set request,response encode.
     */
    private void setEncoding(HttpServletRequest request,
            HttpServletResponse response) {
        try {
            request.setCharacterEncoding("UTF-8");
            response.setCharacterEncoding("UTF-8");
            response.setContentType("text/html;charset=UTF-8");
        } catch (UnsupportedEncodingException e1) {
            e1.printStackTrace();
        }
    }
}
